Skip to content

POC: Add config-driven sensor alert dashboard - #322

Open
jirhiker wants to merge 2 commits into
stagingfrom
sensor-dashboard
Open

POC: Add config-driven sensor alert dashboard#322
jirhiker wants to merge 2 commits into
stagingfrom
sensor-dashboard

Conversation

@jirhiker

@jirhiker jirhiker commented Jul 31, 2026

Copy link
Copy Markdown
Member

POC: ⚠️ Do not merge. This is poc/mock up. gauge user interest/engagement before proceeding

Unified alert dashboard for telemetered sensor sources, starting with Van Essen Diver and Wellntel. Serves the two operator questions: is every logger healthy, and what new data is waiting to be ingested.

Adding a source is a config change only

A source is declared in src/config/sensor-sources/sources/ via defineSensorSource() — transport, vendor field mapping, displayed metrics, alert rules, ingestion target. The alert engine, device grids, and mock provider all read from that registry and contain no per-source branching.

Verified live: Van Essen renders Battery/Signal/Memory/Water level/Temp columns and Wellntel renders Battery/Signal/Depth/Confidence, purely from their configs.

Declarative alert rules

Four kinds — stale, threshold, range, gap — so new failure modes are config, not code. Beyond Zod parsing, defineSensorSource() rejects three things that would otherwise yield a rule that silently never fires:

  • rules referencing an undeclared metric
  • duplicate rule ids
  • a critical bound less severe than its warning bound

Backend does not exist yet

OcotilloAPI has no telemetry ingestion — wellntel and van essen both return 0 grep hits, and transfers/ is a deprecated one-shot CSV migration. The expected contract is documented in src/interfaces/sensor-dashboard/index.ts:

GET  {basePath}                aggregate source status
GET  {basePath}/device         normalized devices
GET  {basePath}/pending        batches awaiting ingestion
POST {basePath}/ingest         trigger a run
GET  {basePath}/ingest/{runId} run status

VITE_SENSOR_MOCK=true serves fixtures generated from the same configs, so the dashboard is fully exercisable until the backend lands. Flipping the flag is the only change needed. It defaults on under test and is set false in .env.production.example.

Reviewer notes

  • Vendor field paths are modelled, not verified against live API responses. Marked TODO(vendor-docs) and correctable in one file per source.
  • Access control: viewing is open to AMP viewers; triggering a run writes to the observation tables and is gated on editor/admin at both the route and the button. src/test/utils/accessControl.test.ts is an intentional guard requiring new routable resources to be declared — updated accordingly.
  • Grid row height is set locally (44px) rather than using the shared settings.rowHeight (27px), which crops the status chips.

Verification

  • tsc --noEmit clean
  • 254/254 tests pass (vitest run --dir src, with the Prism mock server up), including 31 new tests for the alert engine
  • npm run build:fast succeeds
  • Exercised in the browser end to end: triggered an ingestion run and observed running -> poll -> succeeded, 48 records ingested

Two bugs found and fixed by running it: the alert list was flooded by a single failing logger (now capped per device), and alert rows did not name their source when two vendors had a same-numbered point.

🤖 Generated with Claude Code

Introduces a unified dashboard for telemetered sensor sources, starting
with Van Essen Diver and Wellntel. It serves the two operator questions:
is every logger healthy, and what new data is waiting to be ingested.

Adding a source is a configuration change only. A source is declared in
src/config/sensor-sources/sources/ via defineSensorSource(), which
specifies its transport, vendor field mapping, displayed metrics, alert
rules, and ingestion target. The alert engine, device grids, and mock
provider are all driven off that registry and contain no per-source
branching.

Alert rules are declarative (stale / threshold / range / gap) so new
failure modes are expressed as config rather than code. The config is
Zod-validated at import time, and additionally rejects rules pointing at
undeclared metrics, duplicate rule ids, and critical bounds that are less
severe than their warning bound -- each of which would otherwise produce
a rule that silently never fires.

The OcotilloAPI sensor-source endpoints do not exist yet. The expected
contract is documented in src/interfaces/sensor-dashboard, and
VITE_SENSOR_MOCK=true serves fixtures generated from the same configs so
the dashboard is fully exercisable until the backend lands.

Vendor field paths in both source configs are modelled, not verified
against live API responses; they are marked TODO(vendor-docs) and are
correctable in one file per source.

Viewing is open to AMP viewers; triggering an ingestion run writes to the
observation tables and is gated on editor/admin at both the route and the
button.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

Copy link
Copy Markdown

Preview Deployment

Preview URL: https://preview-sensor-dashboard-auejgdbofq-uc.a.run.app

Note: This preview uses the staging API endpoints.

Preview deploys have no sensor backend to talk to, so the dashboard now
defaults to generated fixtures there, keyed off the VITE_APP_ENV=preview
that CD_preview.yml already sets. Staging and production build with their
own VITE_APP_ENV and are unaffected.

The fallback deliberately fails towards real data: a build that forgets
to set VITE_APP_ENV gets mocking off, so it surfaces an honest error
rather than convincing fake sensor readings. VITE_SENSOR_MOCK still
overrides in either direction, and a blank value (which is what Docker
sets for an unpassed ARG) is treated as "no override" rather than as an
explicit opt-out.

Because getting this wrong in the wrong direction would put fabricated
readings on a production dashboard, the resolution rule is pinned by
tests covering preview, staging, production, missing VITE_APP_ENV, and
both override directions.

Also strengthens the in-page notice: it now reads as a warning that every
device and reading is generated and that triggering a run writes nothing,
rather than an aside aimed at developers.
@github-actions

Copy link
Copy Markdown

Preview Deployment

Preview URL: https://preview-sensor-dashboard-auejgdbofq-uc.a.run.app

Note: This preview uses the staging API endpoints.

@jirhiker jirhiker changed the title Add config-driven sensor alert dashboard POC: Add config-driven sensor alert dashboard Jul 31, 2026
@chasetmartin

chasetmartin commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

This is awesome. My two cents and dreaming on top of this (just me, no user research): I could eventually see a "sensor" or "equipment" menu item being the overall home to a few different things like this, such as:

  • "alerts" - like the dashboard here
  • "ingest" - like this demo's awaiting ingest list, with both the API fed sensors but also a running list of the manually-downloaded sensors and the last time sites were visited/downloaded/uploaded - which would then feed hydrograph correction (your wellpy recreation)
  • "manage" - an easy place to edit all equipment installed in wells, updated battery levels or other equipment notes as needed when not updated as part of another data entry workflow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants